unit LogLexer;

interface

procedure Reset();
procedure NewLine(const S: string);
function Eol(): Boolean;

function TakeId(): Boolean;
function TakeWord(): Boolean;
function TakeSymbol(): Boolean;
function TakeTail(): Boolean;
function TakeConst(): Integer;

function LookupId(): Boolean;
function LookupWord(): Boolean;
function LookupSymbol(): Boolean;
function LookupConst(): Integer;

var
  Value: string;
  Column: Integer;
  Line: Integer;

const
  NO_CONST = 0;
  CONST_INTEGER = 1;

implementation

uses SysUtils, MuUtils;

const
  Symbols: TCharSet = ['=', ':', '[', ']',       '+', '-', ')', '(', '*', '/', '.', ','];

var
  CurrentColumn: Integer;
  CurrentLine: string;

function InternalReadSomething(var I: Integer; const StopChars: TCharSet): string;
var
  L: Integer;
  Ch: Char;
begin
  L := 0;
  Ch := #0;
  repeat
    if I + L > Length(CurrentLine) then Break;

    Ch := CurrentLine[I + L];
    if Ch = '#' then Break;
    if Ch in StopChars then Break;

    L := L + 1;
  until False;

  Result := Copy(CurrentLine, I, L);
  if Ch = '#' 
    then I := Length(CurrentLine) + 1
    else I := I + L;
end;

procedure SkipSpaces(var I: Integer);
begin
  InternalReadSomething(I, AllChars - SpaceChars);
end;

function ReadSomething(var I: Integer; const StopChars: TCharSet): string;
begin
  Result := InternalReadSomething(I, StopChars);
  SkipSpaces(I);
end;

function NotIn(I: Integer; CharSet: TCharSet): Boolean;
begin
  Result := not (CurrentLine[I] in CharSet);
end;

procedure NewLine(const S: string);
begin
  Line := Line + 1;
  Column := 0;
  Value := '';

  CurrentColumn := 1;
  CurrentLine := S;

  SkipSpaces(CurrentColumn);
end;

procedure Reset();
begin
  Line := 0;
  Column := 0;
  Value := '';
end;

function Eol(): Boolean;
begin
  Result := CurrentColumn > Length(CurrentLine);
end;


function ReadGeneral(Take: Boolean; FirstChar: TCharSet; OtherChars: TCharSet = []): Boolean;
var
  I: Integer;
begin
  Result := False;
  if OtherChars = [] then OtherChars := FirstChar;
  

  if Eol() then Exit;
  I := CurrentColumn;
  if NotIn(I, FirstChar) then Exit;

  Result := True;
  Column := CurrentColumn;
  I := CurrentColumn + 1;
  Value := CurrentLine[Column] + InternalReadSomething(I, AllChars - OtherChars);
  if Take then
  begin
    CurrentColumn := I;
    SkipSpaces(CurrentColumn);
  end;
end;

function ReadId(Take: Boolean): Boolean;
begin
  Result := ReadGeneral(Take, Letters + ['_'], IdSymbols);
end;

function TakeId(): Boolean;
begin
  Result := ReadId(True);
end;

function LookupId(): Boolean;
begin
  Result := ReadId(False);
end;

function ReadWord(Take: Boolean): Boolean;
begin
  Result := ReadGeneral(Take, AllChars - [#1..#32]);
end;

function TakeWord(): Boolean;
begin
  Result := ReadWord(True);
end;

function LookupWord(): Boolean;
begin
  Result := ReadWord(False);
end;

function ReadSymbol(Take: Boolean): Boolean;
var
  I: Integer;
begin
  Result := False;
  I := CurrentColumn;
  if I > Length(CurrentLine) then Exit;
  if NotIn(I, Symbols) then Exit;

  Result := True;
  Column := CurrentColumn;
  Value := CurrentLine[I];
  I := I + 1;

  if Take then
  begin
    CurrentColumn := I;
    SkipSpaces(CurrentColumn);
  end;
end;

function TakeSymbol(): Boolean;
begin
  Result := ReadSymbol(True);
end;

function LookupSymbol(): Boolean;
begin
  Result := ReadSymbol(False);
end;

function TakeTail(): Boolean;
begin
  Value := '';
  ReadGeneral(True, AllChars - ['#']);
  Value := Trim(Value);
  Result := True;
end;

function ReadConst(Take: Boolean): Integer;
var
  I: Integer;

  function ReadNumeric(): Integer;
  begin
    Value := InternalReadSomething(I, AllChars - ['0'..'9']);
    Result := CONST_INTEGER;
  end;

begin
  Result := NO_CONST;
  I := CurrentColumn;
  if I > Length(CurrentLine) then Exit;

  case CurrentLine[I] of
    '0'..'9': Result := ReadNumeric();
    else Exit;
  end;

  if Take then
  begin
    CurrentColumn := I;
    SkipSpaces(CurrentColumn);
  end;
end;

function LookupConst(): Integer;
begin
  Result := ReadConst(False);
end;

function TakeConst(): Integer;
begin
  Result := ReadConst(True);
end;


end.
